package com.pactera.jep.service.sys.web.controller;

import com.alibaba.fastjson.JSONArray;
import com.pactera.jep.autoconfigure.web.controller.BaseRestController;
import com.pactera.jep.commons.CollectionUtils;
import com.pactera.jep.service.sys.service.FTPService;
import com.pactera.jep.service.sys.service.FtpAttachmentService;
import com.pactera.jep.sys.model.Attachment;
import com.pactera.jep.sys.model.AttachmentType;
import com.pactera.jep.sys.service.AppConfigService;
import com.pactera.jep.sys.service.AttachmentTypeService;
import com.pactera.jep.sys.service.PKeyService;
import com.pactera.jep.sys.util.AttachmentUtil;
import com.pactera.jep.web.code.WebCode;
import com.pactera.jep.web.code.WebHeaderCode;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.net.ftp.FTPClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import springfox.documentation.annotations.ApiIgnore;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping(value = "/attachment")
@ConditionalOnProperty(name="save.type", havingValue="FTP", matchIfMissing=false)
public class AttachmentRestController  extends BaseRestController<Attachment> {
	
	@Autowired
	@Qualifier("ftpAttachmentService")
	private FtpAttachmentService attachmentService;
	
	@RequestMapping(value = "/select")
	public String select(String entityId,
			String uploadSession, String entityType){
		List<Attachment> attachments = attachmentService.select(entityId, uploadSession, entityType);
		if(CollectionUtils.isNotEmpty(attachments)){
			return JSONArray.toJSONString(attachments);
		}
		return  null;
	}

	Logger logger = LoggerFactory.getLogger(AttachmentRestController.class);

	private static final String			ENTITY_ID_NOT_ASSIGNED	= "ENTITY_ID_NOT_ASSIGNED";

	@Autowired
	private AttachmentTypeService attachmentTypeService;

	@Autowired
	private PKeyService pKeyService;

	@Autowired
	private AppConfigService appConfigService;
	
	@Autowired
	private FTPService ftpService;

	/**
	 * 初始化上传参数
	 * 插件初始化时，调用该方法获取如下参数：
	 * 1. uploadSession	上传会话
	 * 2. maxFiles		上传文件个数限制
	 * 3. maxFilesize	上传文件大小限制
	 * 4. includes		允许上传的文件类型, 逗号分隔
	 * 5. excludes		禁止上传的文件类型， 逗号分隔
	 * @param request
	 * @param response
	 * @param model
	 * @return
	 */
	@PostMapping(value="/params")
	public Map<String, Object> params(HttpServletRequest request, HttpServletResponse response, Model model){
		Map<String, Object> tips = new HashMap<String, Object>();

		/*生成uploadSession*/
		String uploadSession = pKeyService.getUUIDKey();
		/*单次允许上传的文件个数*/
		int maxFiles = AttachmentUtil.getMaxFiles();
		/*获得上传文件大小限制(MB)*/
		int maxFilesize = AttachmentUtil.getMaxSizeInMB();
		/*获得允许上传的文件类型*/
		String includes = appConfigService.getConfigValue("UPLOADINCTYPES");
		/*获得禁止上传的文件类型*/
		String excludes = appConfigService.getConfigValue("UPLOADEXCTYPES");
		tips.put("uploadSession", uploadSession);
		tips.put("maxFiles", maxFiles);
		tips.put("maxFileSize", maxFilesize);
		tips.put("includes", includes);
		tips.put("excludes", excludes);
		return tips;
	}

	/**
	 * 删除单个附件
	 * @param request
	 * @param response
	 * @param model
	 * @return
	 */
	@PostMapping(value="/remove/{id}")
	public Map<String, Object> remove(@PathVariable String id, HttpServletRequest request, HttpServletResponse response, Model model){
		Map<String, Object> tips = new HashMap<String, Object>();
		try
		{
			attachmentService.deleteById(id);
			tips.put(WebCode.SUCCESS, true);
			tips.put(WebCode.MSG, "删除成功.");
		} catch(Exception ex)
		{
			ex.printStackTrace();
			tips.put(WebCode.SUCCESS, false);
			tips.put(WebCode.MSG, "删除失败.");
		}
		return tips;
	}

	/**
	 * 查询已有的附件
	 * @param entityId
	 * @param uploadSession
	 * @param request
	 * @param response
	 * @param model
	 * @return
	 */
	@PostMapping(value="/queryAttachment")
	public Map<String, Object> query(String entityId, String uploadSession, String entityType, HttpServletRequest request, HttpServletResponse response, Model model){
		Map<String, Object> tips = new HashMap<String, Object>();
		List<Attachment> list = null;
		try
		{
			list = attachmentService.select(entityId, uploadSession, entityType);
		} catch(Exception ex)
		{
			list = new ArrayList<>();
		}
		tips.put(WebCode.SUCCESS, true);
		tips.put("files", list);
		return tips;
	}
	
	@PostMapping(value="/upload")
	public Map<String, Object> upload(HttpServletRequest request, MultipartFile file, @ApiIgnore @RequestHeader(WebHeaderCode.X_USER_TOKEN) String token) {
		Map<String, Object> tips = new HashMap<String, Object>();
		if (file == null || file.isEmpty()) {
			tips.put(WebCode.SUCCESS, false);
			tips.put(WebCode.MSG, "没有获取到上传文件！");
		} else {
			// 获取到文件之后获取其它相关属性
			String attachTypeCode = request.getParameter("attachTypeCode");
			String entityId = request.getParameter("entityId");
			String uploadSession = request.getParameter("uploadSession");
			
			/**
			 * js传递可能会传递一个undefined过来
			 */
			if ("undefined".equals(attachTypeCode)) {
				attachTypeCode = "";
			}
			
			if (StringUtils.isEmpty(entityId) || "undefined".equals(entityId)) {
				entityId = ENTITY_ID_NOT_ASSIGNED;
			}
			if (StringUtils.isNotBlank(attachTypeCode)) {
				int fileSizeInMb = AttachmentUtil.getMaxSizeInMB();
				long fileSize = AttachmentUtil.getMaxSizeInByte();
				
				boolean proceed = true;
				// 首先判断文件的大小和格式是否合法
				if (!AttachmentUtil.accept(file)) {
					tips.put(WebCode.SUCCESS, false);
					tips.put(WebCode.MSG, "不允许上传的文件类型.");
					proceed = false;
				}
				
				if (fileSize != -1 && file.getSize() > fileSize) {
					tips.put(WebCode.SUCCESS, false);
					tips.put(WebCode.MSG, "文件大小超过限制[" + fileSizeInMb + "(MB)].");
					proceed = false;
				}
				// 如果可以处理，那么上传文件至FTP
				if (proceed) {
					String staffCode = getStaffCode(token);
					List<Attachment> list = attachmentService.upload(staffCode, entityId, attachTypeCode, uploadSession, file);
					tips.put(WebCode.SUCCESS, true);
					tips.put(WebCode.MSG, "上传成功。");
					tips.put("files", list);
				}
			} else {
				tips.put(WebCode.SUCCESS, false);
				tips.put(WebCode.MSG, "上传失败，未定义附件类型。");
			}
		}
		return tips;
	}

	/**
	 * 下载文件
	 * @param request
	 * @param response
	 */
	@RequestMapping(value="/download/{id}")
	public void download(@PathVariable String id, HttpServletRequest request, HttpServletResponse response){
		response.setContentType("text/html;charset=utf-8");
		response.setContentType("application/x-msdownload;charset=utf-8");

		InputStream in = null;
		ServletOutputStream out = null;
		FTPClient ftpClient = null;
		try {
			
			// 首先获取到Attachment对象
			Attachment attachment = attachmentService.get(id);
			if (attachment != null) {
				// 然后从ftp中下载文件至临时目录中
				String remoteFile = attachment.getSaveFileName();
				Map<InputStream, FTPClient> openResult = ftpService.getAsStream(attachment.getUploadPath(), remoteFile);
//				String localFile = "/tmp/"   + remoteFile;
				// 通过InputStream的方式拿到远程文件的输入流
				in = openResult.keySet().iterator().next();
				ftpClient = openResult.values().iterator().next();
				// 然后通过流将数据写回去
				if (in != null) {
					String srcName = attachment.getSrcFileName() + "." + attachment.getFileFormat();
//					String userAgent = request.getHeader("User-Agent");
					String conFileName = URLEncoder.encode(srcName,"UTF-8");
					// 解决中文乱码的问题
					response.setHeader("Content-Disposition", "attachment; filename*=UTF8''" + conFileName);
					response.setContentLengthLong(attachment.getFileSize());
					response.setCharacterEncoding("UTF-8");
					out = response.getOutputStream();
					byte[] data = new byte[2048];
					int count = -1;
					while((count = in.read(data)) != -1) {
						out.write(data, 0, count);
					}
					out.flush();
				}
			}
		} catch (Exception e) {
			logger.error("下载失败：" + e.getMessage());
		} finally {
			try {
				if (in != null)
					in.close();
				if (out != null)
					out.close();
			} catch (IOException e) {
				logger.error("关闭IO流缓冲失败：" + e.getMessage());
			}
			ftpService.closeClientQuietly(ftpClient);
		}
	}
	
	@RequestMapping(value="/preview/img/{id}")
	public void previewImage(@PathVariable String id, HttpServletRequest request, HttpServletResponse response) {
		// 首先获取到附件对象
		Attachment attachment = attachmentService.get(id);
		// 获取到上传路径和上传的文件名
		String uploadPath = attachment.getUploadPath();
		// saveFileName中已经带有文件格式就不需要再添加格式了
		String saveFileName = attachment.getSaveFileName();
		Map<InputStream, FTPClient> openResult = ftpService.getAsStream(uploadPath, saveFileName);
		// 然后打开一个输入流对象从FTP服务器接收文件内容
		try(InputStream in = openResult.keySet().iterator().next();PrintWriter pw = response.getWriter()) {
			// 因为输入流可能开始的时候avaliable为空，所以不能够通过avaliable()来判断流是否为空
			if (in != null) {
				// 首先读取到图片内容
				List<byte[]> result = new ArrayList<>();
				int total = 0;
				byte[] d = new byte[2048];
				// 因为inputstream不一定可以一次读取完成，所以不能用avaliable来设置数组大小
				while (true) {
					int count = -1;
					if ((count = in.read(d)) != -1) {
						total += count;
						result.add(Arrays.copyOf(d, count));
					} else {
						break;
					}
				}
				if (total == 0) {
					logger.error("通过流获取到的文件内容为空！");
					throw new IOException("未能从输入流中获取文件内容！");
				}
				byte[] data = new byte[total];
				int pos = 0;
				for (byte[] item : result) {
					int length = item.length;
					System.arraycopy(item, 0, data, pos, length);
					pos += length;
				}
				// 读取data，并将data转换成base64码
				Base64 encoder = new Base64();
				String base64 = encoder.encodeAsString(data);
				System.out.println("data is ==> " + base64);
				if (StringUtils.isNotBlank(base64)) {
					// 组装data协议
					String dataProtocal = "data:image/" + attachment.getFileFormat().toLowerCase() + ";base64,";
					dataProtocal += base64;
					pw.write(dataProtocal);
				} else {
					pw.write("ERROR");
				}
			} else {
				logger.error("未能获取到文件{}的输入流!", saveFileName);
			}
		} catch (IOException e) {
			logger.error("向流中写入数据出现异常：{}", e.getMessage(), e);
		} finally {
			ftpService.closeClientQuietly(openResult.values().iterator().next());
		}
	}


	/**
	 * 跳转到附件浏览页面
	 * @return
	 */
	@RequestMapping(value="/image/{id}")
	public void image(@PathVariable String id, HttpServletRequest request, HttpServletResponse response){
		Attachment attachment = attachmentService.get(id);

		BufferedInputStream bis = null;
		BufferedOutputStream bos = null;
		try {
			String downLoadPath = AttachmentUtil.getUploadPath();
			if(attachment != null){
				AttachmentType type = attachmentTypeService.get(attachment.getAttachTypeCode());
				response.setContentType("image/" + attachment.getFileFormat());

				String saveName = attachment.getSaveFileName() + "." + attachment.getFileFormat();
				String savePath = type.getAttachPath();
				if(StringUtils.isNotBlank(savePath)){
					downLoadPath += File.separator + savePath + File.separator;
				}
				if(StringUtils.isNotBlank(saveName))
					downLoadPath += saveName;

				File file = new File(downLoadPath);
				InputStream in = null;
				if(!file.exists())
				{
					in = null;//ResourceUtil.getResourceAsStream("com/pactera/jep/console/sys/web/controller/timg.jpg");
				}
				else
				{
					in = new FileInputStream(file);
				}

				bis = new BufferedInputStream(in);
				bos = new BufferedOutputStream(response.getOutputStream());
				byte[] buff = new byte[2048];
				int bytesRead;
				while ((bytesRead = bis.read(buff, 0, buff.length)) != -1){
					bos.write(buff, 0, bytesRead);
				}
				bos.flush();
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (bis != null)
					bis.close();
				if (bos != null)
					bos.close();
			} catch (IOException e) {
				logger.error("关闭IO流缓冲失败：" + e.getMessage());
			}
		}
	}


	/**
	 * 查看文本内容
	 * @param id
	 * @param model
	 * @return
	 */
	@RequestMapping(value="/text/{id}")
	public Map<String, Object> text(@PathVariable String id, Model model){
		Map<String, Object> map = new HashMap<>();
		Attachment attachment = attachmentService.get(id);
		StringBuffer buf = new StringBuffer();
		BufferedReader reader = null;

		try {
			String downLoadPath = AttachmentUtil.getUploadPath();
			if(attachment != null){
				AttachmentType type = attachmentTypeService.get(attachment.getAttachTypeCode());

				String saveName = attachment.getSaveFileName() + "." + attachment.getFileFormat();
				String savePath = type.getAttachPath();
				if(StringUtils.isNotBlank(savePath)){
					downLoadPath += File.separator + savePath + File.separator;
				}
				if(StringUtils.isNotBlank(saveName))
					downLoadPath += saveName;

				reader = new BufferedReader(new InputStreamReader(new FileInputStream(downLoadPath), "UTF-8"));
				String tmp = null;
				while((tmp = reader.readLine()) != null)
				{
					buf.append(tmp).append("\r\n");
				}

				map.put(WebCode.SUCCESS, true);
				map.put("content", buf.toString());
			}
		} catch (Exception e) {

			logger.info("读取文件内容失败。");
			map.put(WebCode.SUCCESS, false);

		} finally {
			try {
				if (reader != null)
					reader.close();
			} catch (IOException e) {
				logger.error("关闭IO流缓冲失败：" + e.getMessage());
			}
		}

		return map;
	}

	/**
	 * 查看文本内容
	 * @param model
	 * @return
	 */
	@RequestMapping(value="/associate")
	public Map<String, Object> associate(String entityId, String uploadSession, Model model){

		Map<String, Object> map = new HashMap<>();

		try {

			int count = attachmentService.associate(entityId, uploadSession);
			map.put(WebCode.SUCCESS, true);
			map.put(WebCode.MSG, "关联成功.");
			map.put("count", count);

		} catch (Exception e) {
			e.printStackTrace();
			map.put(WebCode.SUCCESS, false);
			map.put(WebCode.MSG, "关联失败.");
		}

		return map;

	}
	
}
